package socks

import (
	"errors"
	"fmt"
	"gitee.com/simple-set/simple.io/src/process/codec"
)

type AuthType byte // 认证方法

const (
	NoAuth        AuthType = 0   // 无需认证
	GssAPIAuth    AuthType = 1   // GSS 认证
	UserPassAuth  AuthType = 2   // 账号/密码认证
	IanaAuthStart AuthType = 3   // iana 认证开始标志
	IanaAuthEnd   AuthType = 127 // iana 认证结束标志
	NoAcceptable  AuthType = 255 // 无可用认证方式
)

type AuthState byte // 认证结果

const (
	SUCCESS AuthState = 0 // 认证成功
	FIELD   AuthState = 1 // 认证失败
)

// Authenticator 认证器接口
type Authenticator interface {
	AuthMethod() AuthType             // 支持的认证类型
	Challenge(user, pass string) bool // 认证结果
}

// Authentication 身份认证
type Authentication struct {
	noAuth         bool            // 允许无认证状态
	authenticators []Authenticator // 身份认证器
}

func NewAuthentication() *Authentication {
	return &Authentication{noAuth: true}
}

// Auth 身份认证, 按照客户端的认证方法顺序匹配，一旦匹配到合适的认证方式, 则终止匹配, 然后执行身份验证并返回结果
func (a *Authentication) Auth(handshake *Handshake) error {
	for _, authType := range handshake.authTypes {
		if authType == NoAuth && a.noAuth {
			// 允许无身份认证
			handshake.session.Logger.Debug("socks auth, no need for identity authentication")
			return a.authTypeConfirm(NoAuth, handshake)
		}
		if authenticator := a.MatchAuthenticator(authType); authenticator != nil {
			// 获取认证器并执行
			switch authenticator.AuthMethod() {
			case GssAPIAuth:
				return a.gssAPIAuth(handshake, authenticator)
			case UserPassAuth:
				return a.userPassAuth(handshake, authenticator)
			}
		}
	}
	// 无可用的身份认证器
	if err := a.authTypeConfirm(NoAcceptable, handshake); err != nil {
		return err
	}
	handshake.session.Logger.Debug("socks auth, Negotiation failed, no authentication device available")
	return errors.New("no Acceptable")
}

// 账号密码认证方式
func (a *Authentication) userPassAuth(handshake *Handshake, authenticator Authenticator) error {
	handshake.session.Logger.Debug("Negotiation successful, use account password authentication method")

	if err := a.authTypeConfirm(UserPassAuth, handshake); err != nil {
		return err
	}
	user, pass, err := a.decoderUserPass(handshake.buff)
	if err != nil {
		return err
	}

	if authenticator.Challenge(user, pass) {
		// 认证通过
		return a.authStateConfirm(handshake, SUCCESS)
	}
	if err := a.authStateConfirm(handshake, FIELD); err != nil {
		// 认证拒绝
		return err
	}
	return fmt.Errorf("[%s] verification failed", user)
}

// TODO GSSAPI认证方式, 待实现
func (a *Authentication) gssAPIAuth(handshake *Handshake, _ Authenticator) error {
	handshake.session.Logger.Debug("Negotiation successful, gssAPI authentication")
	return nil
}

// 认证类型确认
func (a *Authentication) authTypeConfirm(authType AuthType, handshake *Handshake) error {
	if err := handshake.buff.WriteBytes([]byte{byte(handshake.version), byte(authType)}); err != nil {
		return err
	}
	return handshake.buff.Flush()
}

// 认证结果确认
func (a *Authentication) authStateConfirm(handshake *Handshake, state AuthState) error {
	if err := handshake.buff.WriteBytes([]byte{1, byte(state)}); err != nil {
		return err
	}
	return handshake.buff.Flush()
}

// SetNoAuth 是否允许无认证
func (a *Authentication) SetNoAuth(noAuth bool) {
	a.noAuth = noAuth
}

// AddAuthenticator 添加认证器
func (a *Authentication) AddAuthenticator(authenticator Authenticator) {
	if a.authenticators == nil {
		a.authenticators = make([]Authenticator, 0)
	}
	a.authenticators = append(a.authenticators, authenticator)
}

// MatchAuthenticator  匹配认证器
func (a *Authentication) MatchAuthenticator(authType AuthType) Authenticator {
	if a.authenticators == nil || len(a.authenticators) == 0 {
		return nil
	}
	for _, authenticator := range a.authenticators {
		if authenticator.AuthMethod() == authType {
			return authenticator
		}
	}
	return nil
}

// 从缓冲区读取数据, 尝试解码账号和密码，RFC1929 https://datatracker.ietf.org/doc/html/rfc1929
func (a *Authentication) decoderUserPass(buf *codec.ByteBuf) (string, string, error) {
	if ver, err := buf.ReadByte(); err != nil {
		return "", "", err
	} else if ver != byte(1) {
		return "", "", errors.New("invalid version")
	}

	// 读取账号
	ulen, err := buf.ReadByte()
	if err != nil {
		return "", "", err
	}
	user, err := buf.ReadBytes(int(ulen))
	if err != nil {
		return "", "", err
	}
	// 读取密码
	plen, err := buf.ReadByte()
	if err != nil {
		return "", "", err
	}
	pass, err := buf.ReadBytes(int(plen))
	if err != nil {
		return "", "", err
	}

	return string(user), string(pass), nil
}
